home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
ip
/
ka9q
/
src.arc
/
SOCKET.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-31
|
47KB
|
1,971 lines
#include <stdio.h>
#ifdef __STDC__
#include <stdarg.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "iface.h"
#include "ip.h"
#include "tcp.h"
#include "udp.h"
#include "ax25.h"
#include "lapb.h"
#include "netrom.h"
#include "nr4.h"
#include "proc.h"
#include "usock.h"
#include "socket.h"
#include "config.h"
extern int errno;
static void autobind __ARGS((int s,int af));
static int checkaddr __ARGS((int type,char *name,int namelen));
static struct usock *itop __ARGS((int s));
static void rip_recv __ARGS((struct raw_ip *rp));
static void s_nrcall __ARGS((struct nr4cb *cb,int cnt));
static void s_nscall __ARGS((struct nr4cb *cb,int old,int new));
static void s_ntcall __ARGS((struct nr4cb *cb,int cnt));
static void s_trcall __ARGS((struct tcb *tcb,int cnt));
static void s_tscall __ARGS((struct tcb *tcb,int old,int new));
static void s_ttcall __ARGS((struct tcb *tcb,int cnt));
static void s_urcall __ARGS((struct udp_cb *udp,int cnt));
static void trdiscard __ARGS((struct tcb *tcb,int cnt));
static int16 Lport = 1024;
char *Socktypes[] = {
"Not Used",
"TCP",
"UDP",
"AX25 I",
"AX25 UI",
"Raw IP",
"NETROM3",
"NETROM"
};
static char Badsocket[] = "Bad socket";
struct usock *Usock; /* Socket entry array */
int Nusock = DEFNSOCK; /* Number of socket entries */
/* The following two variables are needed because there can be only one
* socket listening on each of the AX.25 modes (I and UI)
*/
#ifdef AX25
int Axi_sock = -1; /* Socket number listening for AX25 connections */
static int Axui_sock = -1; /* Socket number listening for AX25 UI frames */
static struct mbuf *Bcq; /* Queue of incoming UI frames */
/* Function that handles incoming UI frames from lapb.c */
void
beac_input(iface,src,bp)
struct iface *iface;
struct ax25_addr *src;
struct mbuf *bp;
{
struct mbuf *hdr;
struct sockaddr_ax *sax;
if(Axui_sock == -1){
/* Nobody there to read it */
free_p(bp);
} else {
if((hdr = pushdown(NULLBUF,sizeof(struct sockaddr_ax))) == NULLBUF){
free_p(bp);
return;
}
sax = (struct sockaddr_ax *)hdr->data;
sax->sax_family = AF_AX25;
ASSIGN(sax->ax25_addr,*src);
strncpy((char *)sax->iface,iface->name,ILEN);
hdr->next = bp;
enqueue(&Bcq,hdr);
}
}
#endif
/* Initialize user socket array */
void
sockinit()
{
if(Usock != NULLUSOCK)
return; /* Already initialized */
Usock = (struct usock *)calloc(Nusock,sizeof(struct usock));
}
/* Create a user socket, return socket index
* The mapping to actual protocols is as follows:
*
*
* ADDRESS FAMILY Stream Datagram Raw Seq. Packet
*
* AF_INET TCP UDP IP
* AF_AX25 I-frames UI-frames
* AF_NETROM NET/ROM L3 NET/ROM L4
*/
int
socket(af,type,protocol)
int af; /* Address family */
int type; /* Stream or datagram */
int protocol; /* Used for raw IP sockets */
{
register struct usock *up;
int s;
for(up=Usock;up < &Usock[Nusock];up++)
if(up->type == NOTUSED)
break;
if(up == &Usock[Nusock]){
/* None left */
errno = EMFILE;
return -1;
}
s = up - Usock;
errno = 0;
up->noblock = 0;
up->rdysock = -1;
up->cb.p = NULLCHAR;
up->peername = up->name = NULLCHAR;
up->namelen = up->peernamelen = 0;
up->owner = Curproc;
memset(up->errcodes,0,sizeof(up->errcodes));
switch(af){
#ifdef AX25
case AF_AX25:
switch(type){
case SOCK_STREAM:
up->type = TYPE_AX25I;
break;
case SOCK_DGRAM:
up->type = TYPE_AX25UI;
break;
default:
errno = ESOCKTNOSUPPORT;
break;
}
break;
#endif
#ifdef NETROM
case AF_NETROM:
switch(type){
case SOCK_RAW:
up->type = TYPE_NETROML3;
up->cb.rnr = raw_nr(protocol);
break;
case SOCK_SEQPACKET:
up->type = TYPE_NETROML4;
break;
default:
errno = ESOCKTNOSUPPORT;
break;
}
break;
#endif
case AF_INET:
switch(type){
case SOCK_STREAM:
up->type = TYPE_TCP;
break;
case SOCK_DGRAM:
up->type = TYPE_UDP;
break;
case SOCK_RAW:
up->type = TYPE_RAW;
up->cb.rip = raw_ip(protocol,rip_recv);
up->cb.rip->user = s;
break;
default:
errno = ESOCKTNOSUPPORT;
break;
}
break;
default:
errno = EAFNOSUPPORT;
break;
}
if(errno)
return -1;
return s;
}
/* Attach a local address/port to a socket. If not issued before a connect
* or listen, will be issued automatically
*/
int
bind(s,name,namelen)
int s; /* Socket index */
char *name; /* Local name */
int namelen; /* Length of name */
{
register struct usock *up;
union sp local;
struct socket lsock;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(name == NULLCHAR){
errno = EFAULT;
return -1;
}
if(up->name != NULLCHAR){
/* Bind has already been issued */
errno = EINVAL;
return -1;
}
if(checkaddr(up->type,name,namelen) == -1){
/* Incorrect length or family for chosen protocol */
errno = EAFNOSUPP;
return -1;
}
/* Stash name in an allocated block */
up->namelen = namelen;
up->name = malloc(namelen);
memcpy(up->name,name,namelen);
/* Create control block for datagram sockets */
switch(up->type){
case TYPE_UDP:
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
up->cb.udp = open_udp(&lsock,s_urcall);
up->cb.udp->user = s;
break;
#ifdef AX25
case TYPE_AX25UI:
if(Axui_sock != -1){
errno = EADDRINUSE;
return -1;
}
Axui_sock = s;
break;
#endif
}
return 0;
}
/* Post a listen on a socket */
int
listen(s,backlog)
int s; /* Socket index */
int backlog; /* 0 for a single connection, 1 for multiple connections */
{
register struct usock *up;
union sp local;
struct socket lsock;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p != NULLCHAR){
errno = EISCONN;
return -1;
}
switch(up->type){
case TYPE_TCP:
if(up->name == NULLCHAR)
autobind(s,AF_INET);
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
up->cb.tcb = open_tcp(&lsock,NULLSOCK,
backlog ? TCP_SERVER:TCP_PASSIVE,0,
s_trcall,s_ttcall,s_tscall,0,s);
break;
#ifdef AX25
case TYPE_AX25I:
if(up->name == NULLCHAR)
autobind(s,AF_AX25);
if(s != Axi_sock){
errno = EOPNOTSUPP;
return -1;
}
local.ax = (struct sockaddr_ax *)up->name;
up->cb.ax25 = open_ax25(NULLIF,&local.ax->ax25_addr,NULLAXADDR,
backlog ? AX_SERVER:AX_PASSIVE,0,
s_arcall,s_atcall,s_ascall,s);
break;
#endif
#ifdef NETROM
case TYPE_NETROML4:
if(up->name == NULLCHAR)
autobind(s,AF_NETROM);
local.nr = (struct sockaddr_nr *)up->name;
up->cb.nr4 = open_nr4(&local.nr->nr_addr,NULLNRADDR,
backlog ? AX_SERVER:AX_PASSIVE,
s_nrcall,s_ntcall,s_nscall,s);
break;
#endif
default:
/* Listen not supported on datagram sockets */
errno = EOPNOTSUPP;
return -1;
}
return 0;
}
/* Initiate active open. For datagram sockets, merely bind the remote address. */
int
connect(s,peername,peernamelen)
int s; /* Socket index */
char *peername; /* Peer name */
int peernamelen; /* Length of peer name */
{
register struct usock *up;
union cb cb;
union sp local,remote;
struct socket lsock,fsock;
#ifdef AX25
struct iface *iface;
#endif
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(peername == NULLCHAR){
/* Connect must specify a remote address */
errno = EFAULT;
return -1;
}
if(checkaddr(up->type,peername,peernamelen) == -1){
errno = EAFNOSUPPORT;
return -1;
}
/* Raw socket control blocks are created in socket() */
if(up->type != TYPE_RAW && up->type != TYPE_NETROML3 &&
up->cb.p != NULLCHAR){
errno = EISCONN;
return -1;
}
if((up->peername = malloc(peernamelen)) == NULLCHAR){
errno = ENOMEM;
return -1;
}
memcpy(up->peername,peername,peernamelen);
up->peernamelen = peernamelen;
/* Set up the local socket structures */
if(up->name == NULLCHAR){
switch(up->type){
case TYPE_TCP:
case TYPE_UDP:
case TYPE_RAW:
autobind(s,AF_INET);
break;
case TYPE_AX25I:
case TYPE_AX25UI:
autobind(s,AF_AX25);
break;
case TYPE_NETROML3:
case TYPE_NETROML4:
autobind(s,AF_NETROM);
break;
}
}
switch(up->type){
case TYPE_TCP:
/* Construct the TCP-style ports from the sockaddr structs */
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
remote.in = (struct sockaddr_in *)up->peername;
fsock.address = remote.in->sin_addr.s_addr;
fsock.port = remote.in->sin_port;
/* Open the TCB in active mode */
up->cb.tcb = open_tcp(&lsock,&fsock,TCP_ACTIVE,0,
s_trcall,s_ttcall,s_tscall,0,s);
/* Wait for the connection to complete */
while((cb.tcb = up->cb.tcb) != NULLTCB && cb.tcb->state != ESTABLISHED){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.tcb == NULLTCB){
/* Probably got refused */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
break;
case TYPE_UDP:
case TYPE_AX25UI:
case TYPE_NETROML3:
case TYPE_RAW:
/* Control block already created by bind() */
break;
#ifdef AX25
case TYPE_AX25I:
local.ax = (struct sockaddr_ax *)up->name;
remote.ax = (struct sockaddr_ax *)up->peername;
if((iface = if_lookup(remote.ax->iface)) == NULLIF){
errno = EINVAL;
return -1;
}
/* If we already have an AX25 link we can use it */
if((up->cb.ax25 = find_ax25(&remote.ax->ax25_addr)) != NULLAX25
&& up->cb.ax25->state != DISCONNECTED &&
up->cb.ax25->user == -1) {
up->cb.ax25->user = s;
up->cb.ax25->r_upcall = s_arcall;
up->cb.ax25->t_upcall = s_atcall;
up->cb.ax25->s_upcall = s_ascall;
if(up->cb.ax25->state == CONNECTED
|| up->cb.ax25->state == RECOVERY)
return 0;
}
else up->cb.ax25 = open_ax25(iface,&local.ax->ax25_addr,
&remote.ax->ax25_addr,AX_ACTIVE,
Axwindow,s_arcall,s_atcall,s_ascall,s);
/* Wait for the connection to complete */
while((cb.ax25 = up->cb.ax25) != NULLAX25 && cb.ax25->state != CONNECTED){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.ax25 == NULLAX25){
/* Connection probably already exists */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
break;
#endif
#ifdef NETROM
case TYPE_NETROML4:
local.nr = (struct sockaddr_nr *)up->name;
remote.nr = (struct sockaddr_nr *)up->peername;
up->cb.nr4 = open_nr4(&local.nr->nr_addr,&remote.nr->nr_addr,
AX_ACTIVE,s_nrcall,s_ntcall,s_nscall,s);
/* Wait for the connection to complete */
while((cb.nr4 = up->cb.nr4) != NULLNR4CB && cb.nr4->state != NR4STCON){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.nr4 == NULLNR4CB){
/* Connection probably already exists */
free(up->peername);
up->peername = NULLCHAR;
errno = ECONNREFUSED;
return -1;
}
break;
#endif
}
return 0;
}
/* Wait for a connection. Valid only for connection-oriented sockets. */
int
accept(s,peername,peernamelen)
int s; /* Socket index */
char *peername; /* Peer name */
int *peernamelen; /* Length of peer name */
{
int i;
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = EOPNOTSUPP;
return -1;
}
/* Accept is valid only for stream sockets */
switch(up->type){
case TYPE_TCP:
case TYPE_AX25I:
case TYPE_NETROML4:
break;
default:
errno = EOPNOTSUPP;
return -1;
}
/* Wait for the state-change upcall routine to signal us */
while(up->cb.p != NULLCHAR && up->rdysock == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(up->cb.p == NULLCHAR){
/* Blown away */
errno = EBADF;
return -1;
}
i = up->rdysock;
up->rdysock = -1;
up = &Usock[i];
if(peername != NULLCHAR && peernamelen != NULL){
*peernamelen = min(up->peernamelen,*peernamelen);
memcpy(peername,up->peername,*peernamelen);
}
return i;
}
/* Low-level receive routine. Passes mbuf back to user; more efficient than
* higher-level functions recv() and recvfrom(). Datagram sockets ignore
* the len parameter.
*/
int
recv_mbuf(s,bpp,len,flags,from,fromlen)
int s; /* Socket index */
struct mbuf **bpp; /* Place to stash receive buffer */
int len; /* Maximum length to read (0 implies all) */
int flags; /* Unused; will control out-of-band data, etc */
char *from; /* Peer address (only for datagrams) */
int *fromlen; /* Length of peer address */
{
register struct usock *up;
int cnt;
union cb cb;
struct socket fsocket;
#ifdef NETROM
struct nr3hdr n3hdr;
#endif
union sp remote;
struct ip ip;
struct mbuf *bp;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
switch(up->type){
case TYPE_TCP:
while((cb.tcb = up->cb.tcb) != NULLTCB
&& cb.tcb->r_upcall != trdiscard
&& (cnt = recv_tcp(cb.tcb,&bp,len)) == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.tcb == NULLTCB){
/* Connection went away */
errno = ENOTCONN;
return -1;
} else if(cb.tcb->r_upcall == trdiscard){
/* Receive shutdown has been done */
errno = ENOTCONN; /* CHANGE */
return -1;
}
break;
case TYPE_UDP:
while((cb.udp = up->cb.udp) != NULLUDP
&& (cnt = recv_udp(cb.udp,&fsocket,&bp)) == -1){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.udp == NULLUDP){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){
remote.in = (struct sockaddr_in *)from;
remote.in->sin_family = AF_INET;
remote.in->sin_addr.s_addr = fsocket.address;
remote.in->sin_port = fsocket.port;
*fromlen = SOCKSIZE;
}
break;
case TYPE_RAW:
while((cb.rip = up->cb.rip) != NULLRIP
&& cb.rip->rcvq == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.rip == NULLRIP){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
bp = dequeue(&cb.rip->rcvq);
ntohip(&ip,&bp);
cnt = len_mbuf(bp);
if(from != NULLCHAR && fromlen != (int *)NULL && *fromlen >= SOCKSIZE){
remote.in = (struct sockaddr_in *)from;
remote.in->sin_family = AF_INET;
remote.in->sin_addr.s_addr = ip.source;
remote.in->sin_port = 0;
*fromlen = SOCKSIZE;
}
break;
#ifdef AX25
case TYPE_AX25I:
while((cb.ax25 = up->cb.ax25) != NULLAX25
&& (bp = recv_ax25(cb.ax25,len)) == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.ax25 == NULLAX25){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
cnt = bp->cnt;
break;
case TYPE_AX25UI:
while(s == Axui_sock && Bcq == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(&Bcq) != 0){
errno = EINTR;
return -1;
}
}
if(s != Axui_sock){
errno = ENOTCONN;
return -1;
}
bp = dequeue(&Bcq);
if(from != NULLCHAR && fromlen != NULLINT
&& *fromlen >= sizeof(struct sockaddr_ax)){
pullup(&bp,(char *)from,sizeof(struct sockaddr_ax));
*fromlen = sizeof(struct sockaddr_ax);
} else {
pullup(&bp,NULLCHAR,sizeof(struct sockaddr_ax));
}
cnt = len_mbuf(bp);
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
while((cb.rnr = up->cb.rnr) != NULLRNR
&& cb.rnr->rcvq == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.rnr == NULLRNR){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
bp = dequeue(&cb.rnr->rcvq);
ntohnr3(&n3hdr,&bp);
cnt = len_mbuf(bp);
if(from != NULLCHAR && fromlen != NULLINT
&& *fromlen >= sizeof(struct sockaddr_nr)){
remote.nr = (struct sockaddr_nr *)from;
remote.nr->nr_family = AF_NETROM;
/* The callsign of the local user is not part of
NET/ROM level 3, so that field is not used here */
memcpy(remote.nr->nr_addr.node_call,&n3hdr.source,
AXALEN);
*fromlen = sizeof(struct sockaddr_nr);
}
break;
case TYPE_NETROML4:
while((cb.nr4 = up->cb.nr4) != NULLNR4CB
&& (bp = recv_nr4(cb.nr4,len)) == NULLBUF){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.nr4 == NULLNR4CB){
/* Connection went away */
errno = ENOTCONN;
return -1;
}
cnt = bp->cnt;
break;
#endif
}
if(bpp != NULLBUFP)
*bpp = bp;
return cnt;
}
/* Low level send routine; user supplies mbuf for transmission. More
* efficient than send() or sendto(), the higher level interfaces.
* The "to" and "tolen" parameters are ignored on connection-oriented
* sockets.
*
* In case of error, bp is freed so the caller doesn't have to worry about it.
*/
int
send_mbuf(s,bp,flags,to,tolen)
int s; /* Socket index */
struct mbuf *bp; /* Buffer to send */
int flags; /* not currently used */
char *to; /* Destination, only for datagrams */
int tolen; /* Length of destination */
{
register struct usock *up;
union cb cb;
union sp local,remote;
struct socket lsock,fsock;
int cnt;
if((up = itop(s)) == NULLUSOCK){
free_p(bp);
errno = EBADF;
return -1;
}
if(up->name == NULLCHAR){
/* Automatic local name assignment for datagram sockets */
switch(up->type){
case TYPE_UDP:
case TYPE_RAW:
autobind(s,AF_INET);
break;
case TYPE_AX25UI:
autobind(s,AF_AX25);
break;
case TYPE_NETROML3:
case TYPE_NETROML4:
autobind(s,AF_NETROM);
break;
default:
free_p(bp);
errno = ENOTCONN;
return -1;
}
}
cnt = len_mbuf(bp);
switch(up->type){
case TYPE_TCP:
if((cb.tcb = up->cb.tcb) == NULLTCB){
free_p(bp);
errno = ENOTCONN;
return -1;
}
cnt = send_tcp(cb.tcb,bp);
while((cb.tcb = up->cb.tcb) != NULLTCB &&
cb.tcb->sndcnt > cb.tcb->window){
/* Send queue is full */
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.tcb == NULLTCB){
errno = ENOTCONN;
return -1;
}
break;
case TYPE_UDP:
local.in = (struct sockaddr_in *)up->name;
lsock.address = local.in->sin_addr.s_addr;
lsock.port = local.in->sin_port;
if(to != NULLCHAR)
remote.in = (struct sockaddr_in *)to;
else if(up->peername != NULLCHAR)
remote.in = (struct sockaddr_in *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
fsock.address = remote.in->sin_addr.s_addr;
fsock.port = remote.in->sin_port;
send_udp(&lsock,&fsock,0,0,bp,0,0,0);
break;
case TYPE_RAW:
local.in = (struct sockaddr_in *)up->name;
if(to != NULLCHAR)
remote.in = (struct sockaddr_in *)to;
else if(up->peername != NULLCHAR)
remote.in = (struct sockaddr_in *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
ip_send(local.in->sin_addr.s_addr,remote.in->sin_addr.s_addr,
up->cb.rip->protocol,0,0,bp,0,0,0);
break;
#ifdef AX25
case TYPE_AX25I:
if((cb.ax25 = up->cb.ax25) == NULLAX25){
free_p(bp);
errno = ENOTCONN;
return -1;
}
send_ax25(cb.ax25,bp,PID_NO_L3);
while((cb.ax25 = up->cb.ax25) != NULLAX25 &&
len_q(cb.ax25->txq) > cb.ax25->window){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.ax25 == NULLAX25){
errno = EBADF;
return -1;
}
break;
case TYPE_AX25UI:
local.ax = (struct sockaddr_ax *)up->name;
if(to != NULLCHAR)
remote.ax = (struct sockaddr_ax *)to;
else if(up->peername != NULLCHAR)
remote.ax = (struct sockaddr_ax *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
ax_output(if_lookup(remote.ax->iface),
(char *)&remote.ax->ax25_addr,
(char *)&local.ax->ax25_addr,PID_NO_L3,bp);
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
if(len_mbuf(bp) > NR4MAXINFO) {
free_p(bp);
errno = EMSGSIZE;
return -1;
}
local.nr = (struct sockaddr_nr *)up->name;
if(to != NULLCHAR)
remote.nr = (struct sockaddr_nr *)to;
else if(up->peername != NULLCHAR)
remote.nr = (struct sockaddr_nr *)up->peername;
else {
errno = ENOTCONN;
return -1;
}
/* The NETROM username is always ignored in outgoing traffic */
nr_sendraw((struct ax25_addr *)remote.nr->nr_addr.node_call,
up->cb.rnr->protocol,up->cb.rnr->protocol,bp);
break;
case TYPE_NETROML4:
if((cb.nr4 = up->cb.nr4) == NULLNR4CB) {
free_p(bp);
errno = ENOTCONN;
return -1;
}
if(len_mbuf(bp) > NR4MAXINFO){ /* reject big packets */
free_p(bp);
errno = EMSGSIZE;
return -1;
}
send_nr4(cb.nr4,bp);
while((cb.nr4 = up->cb.nr4) != NULLNR4CB &&
cb.nr4->nbuffered >= cb.nr4->window){
if(up->noblock){
errno = EWOULDBLOCK;
return -1;
} else if(pwait(up) != 0){
errno = EINTR;
return -1;
}
}
if(cb.nr4 == NULLNR4CB){
errno = EBADF;
return -1;
}
break;
#endif
}
return cnt;
}
/* Higher-level receive routine, intended for connection-oriented sockets.
* Can be used with datagram sockets, although the sender id is lost.
*/
int
recv(s,buf,len,flags)
int s; /* Socket index */
char *buf; /* User buffer */
int len; /* Max length to receive */
int flags; /* Unused; will eventually select oob data, etc */
{
struct mbuf *bp;
int cnt;
if(len == 0)
return 0; /* Otherwise would be interp as "all" */
cnt = recv_mbuf(s,&bp,len,flags,NULLCHAR,(int *)NULL);
if(cnt > 0){
cnt = min(cnt,len);
pullup(&bp,buf,cnt);
free_p(bp);
}
return cnt;
}
/* Higher level receive routine, intended for datagram sockets. Can also
* be used for connection-oriented sockets, although from and fromlen are
* ignored.
*/
int
recvfrom(s,buf,len,flags,from,fromlen)
int s; /* Socket index */
char *buf; /* User buffer */
int len; /* Maximum length */
int flags; /* Unused; will eventually select oob data, etc */
char *from; /* Source address, only for datagrams */
int *fromlen; /* Length of source address */
{
struct mbuf *bp;
register int cnt;
cnt = recv_mbuf(s,&bp,len,flags,from,fromlen);
if(cnt > 0){
cnt = min(cnt,len);
pullup(&bp,buf,cnt);
free_p(bp);
}
return cnt;
}
/* High level send routine */
int
send(s,buf,len,flags)
int s; /* Socket index */
char *buf; /* User buffer */
int len; /* Length of buffer */
int flags; /* Unused; will eventually select oob data, etc */
{
register struct mbuf *bp;
char sock[MAXSOCKSIZE];
int i = MAXSOCKSIZE;
if(getpeername(s,sock,&i) == -1)
return -1;
bp = qdata(buf,len);
return send_mbuf(s,bp,flags,sock,i);
}
/* High level send routine, intended for datagram sockets. Can be used on
* connection-oriented sockets, but "to" and "tolen" are ignored.
*/
int
sendto(s,buf,len,flags,to,tolen)
int s; /* Socket index */
char *buf; /* User buffer */
int len; /* Length of buffer */
int flags; /* Unused; will eventually select oob data, etc */
char *to; /* Destination, only for datagrams */
int tolen; /* Length of destination */
{
register struct mbuf *bp;
bp = qdata(buf,len);
return send_mbuf(s,bp,flags,to,tolen);
}
/* Return local name passed in an earlier bind() call */
int
getsockname(s,name,namelen)
int s; /* Socket index */
char *name; /* Place to stash name */
int *namelen; /* Length of same */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(name == NULLCHAR || namelen == (int *)NULL){
errno = EFAULT;
return -1;
}
if(up->name == NULLCHAR){
/* Not bound yet */
*namelen = 0;
return 0;
}
if(up->name != NULLCHAR){
*namelen = min(*namelen,up->namelen);
memcpy(name,up->name,*namelen);
}
return 0;
}
/* Get remote name, returning result of earlier connect() call. */
int
getpeername(s,peername,peernamelen)
int s; /* Socket index */
char *peername; /* Place to stash name */
int *peernamelen; /* Length of same */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->peername == NULLCHAR){
errno = ENOTCONN;
return -1;
}
if(peername == NULLCHAR || peernamelen == (int *)NULL){
errno = EFAULT;
return -1;
}
*peernamelen = min(*peernamelen,up->peernamelen);
memcpy(peername,up->peername,*peernamelen);
return 0;
}
/* Return length of protocol queue, either send or receive. */
int
socklen(s,rtx)
int s; /* Socket index */
int rtx; /* 0 = receive queue, 1 = transmit queue */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return -1;
}
if(rtx < 0 || rtx > 1){
errno = EINVAL;
return -1;
}
switch(up->type){
case TYPE_TCP:
switch(rtx){
case 0:
return up->cb.tcb->rcvcnt;
case 1:
return up->cb.tcb->sndcnt;
}
case TYPE_UDP:
switch(rtx){
case 0:
return up->cb.udp->rcvcnt;
case 1:
return 0;
}
#ifdef AX25
case TYPE_AX25I:
switch(rtx){
case 0:
return len_mbuf(up->cb.ax25->rxq);
case 1: /* Number of packets, not bytes */
return len_q(up->cb.ax25->txq);
}
case TYPE_AX25UI:
switch(rtx){
case 0:
return len_q(Bcq);
case 1:
return 0;
}
#endif
#ifdef NETROM
case TYPE_NETROML3:
switch(rtx){
case 0:
return len_q(up->cb.rnr->rcvq);
case 1:
return 0;
}
case TYPE_NETROML4:
switch(rtx){
case 0:
return len_mbuf(up->cb.nr4->rxq);
case 1: /* Number of packets, not bytes */
return len_q(up->cb.nr4->txq);
}
#endif
case TYPE_RAW:
switch(rtx){
case 0:
return len_q(up->cb.rip->rcvq);
case 1:
return 0;
}
}
return -1;
}
/* Force retransmission. Valid only for connection-oriented sockets. */
int
sockkick(s)
int s; /* Socket index */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return -1;
}
switch(up->type){
case TYPE_TCP:
kick_tcp(up->cb.tcb);
break;
#ifdef AX25
case TYPE_AX25I:
kick_ax25(up->cb.ax25);
break;
#endif
#ifdef NETROM
case TYPE_NETROML4:
kick_nr4(up->cb.nr4);
break;
#endif
default:
/* Datagram sockets can't be kicked */
errno = EOPNOTSUPP;
return -1;
}
return 0;
}
/* Get state of protocol. Valid only for connection-oriented sockets. */
char *
sockstate(s)
int s; /* Socket index */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return NULLCHAR;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return NULLCHAR;
}
switch(up->type){
case TYPE_TCP:
return Tcpstates[up->cb.tcb->state];
#ifdef AX25
case TYPE_AX25I:
return Ax25states[up->cb.ax25->state];
#endif
#ifdef NETROM
case TYPE_NETROML4:
return Nr4states[up->cb.nr4->state];
#endif
default:
/* Datagram sockets don't have state */
errno = EOPNOTSUPP;
return NULLCHAR;
}
/*NOTREACHED*/
}
/* Change owner of socket, return previous owner */
struct proc *
sockowner(s,newowner)
int s; /* Socket index */
struct proc *newowner; /* Process table address of new owner */
{
register struct usock *up;
struct proc *pp;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return NULLPROC;
}
pp = up->owner;
if(newowner != NULLPROC)
up->owner = newowner;
return pp;
}
/* Close down a socket three ways. Type 0 means "no more receives"; this
* replaces the incoming data upcall with a routine that discards further
* data. Type 1 means "no more sends", and obviously corresponds to sending
* a TCP FIN. Type 2 means "no more receives or sends". This I interpret
* as "abort the connection".
*/
int
shutdown(s,how)
int s; /* Socket index */
int how; /* (see above) */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
if(up->cb.p == NULLCHAR){
errno = ENOTCONN;
return -1;
}
switch(up->type){
case TYPE_TCP:
switch(how){
case 0: /* No more receives -- replace upcall */
up->cb.tcb->r_upcall = trdiscard;
break;
case 1: /* Send EOF */
close_tcp(up->cb.tcb);
break;
case 2: /* Blow away TCB */
reset_tcp(up->cb.tcb);
up->cb.tcb = NULLTCB;
break;
}
break;
#ifdef AX25
case TYPE_AX25UI:
close_s(s);
break;
case TYPE_AX25I:
switch(how){
case 0:
case 1: /* Attempt regular disconnect */
disc_ax25(up->cb.ax25);
break;
case 2: /* Blow it away */
reset_ax25(up->cb.ax25);
up->cb.ax25 = NULLAX25;
break;
}
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
close_s(s);
break;
case TYPE_NETROML4:
switch(how){
case 0:
case 1: /* Attempt regular disconnect */
disc_nr4(up->cb.nr4);
break;
case 2: /* Blow it away */
reset_nr4(up->cb.nr4);
up->cb.nr4 = NULLNR4CB;
break;
}
break;
#endif
case TYPE_RAW:
close_s(s);
break;
default:
errno = EOPNOTSUPP;
return -1;
}
psignal(up,0);
return 0;
}
/* Close a socket, freeing it for reuse. Try to do a graceful close on a
* TCP socket, if possible
*/
int
close_s(s)
int s; /* Socket index */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return -1;
}
switch(up->type){
case TYPE_TCP:
if(up->cb.tcb != NULLTCB){ /* In case it's been reset */
up->cb.tcb->r_upcall = trdiscard;
/* Tell the CLOSED upcall there's no more socket */
up->cb.tcb->user = -1;
close_tcp(up->cb.tcb);
}
break;
case TYPE_UDP:
if(up->cb.udp != NULLUDP){
del_udp(up->cb.udp);
}
break;
#ifdef AX25
case TYPE_AX25I:
if(up->cb.ax25 != NULLAX25){
/* Tell the CLOSED upcall there's no more socket */
up->cb.ax25->user = -1;
disc_ax25(up->cb.ax25);
}
break;
case TYPE_AX25UI:
Axui_sock = -1;
free_q(&Bcq);
psignal(&Bcq,0); /* Unblock any reads */
break;
#endif
#ifdef NETROM
case TYPE_NETROML3:
del_rnr(up->cb.rnr);
break;
case TYPE_NETROML4:
if(up->cb.nr4 != NULLNR4CB){
/* Tell the CLOSED upcall there's no more socket */
up->cb.nr4->user = -1;
disc_nr4(up->cb.nr4);
}
break;
#endif
case TYPE_RAW:
del_ip(up->cb.rip);
break;
default:
errno = EOPNOTSUPP;
return -1;
}
free(up->name);
free(up->peername);
up->cb.p = NULLCHAR;
up->name = up->peername = NULLCHAR;
up->type = NOTUSED;
psignal(up,0); /* Wake up anybody doing an accept() or recv() */
return 0;
}
/* Return ASCII string giving reason for connection closing */
char *
sockerr(s)
int s; /* Socket index */
{
register struct usock *up;
if((up = itop(s)) == NULLUSOCK){
errno = EBADF;
return Badsocket;
}
switch(up->type){
#ifdef AX25
case TYPE_AX25I:
if(up->cb.ax25 != NULLAX25)
return NULLCHAR; /* nothing wrong */
return Axreasons[up->errcodes[0]];
#endif
#ifdef NETROM
case TYPE_NETROML4:
if(up->cb.nr4 != NULLNR4CB)
return NULLCHAR; /* nothing wrong */
return Nr4reasons[up->errcodes[0]];
#endif
case TYPE_TCP:
if(up->cb.tcb != NULLTCB)
return NULLCHAR; /* nothing wrong */
return Tcpreasons[up->errcodes[0]];
default:
errno = EOPNOTSUPP; /* not yet, anyway */
return NULLCHAR;
}
}
/* End of user socket primitives, begin user utility subroutines */
/* Receive a newline-terminated line from a socket, returning # chars read */
int
recvline(s,buf,len)
int s; /* Socket index */
char *buf; /* User buffer */
unsigned len; /* Length of buffer */
{
char c;
int cnt = 0;
struct usock *up;
up = itop(s);
while(len-- > 1){
if(recv(s,&c,1,0) != 1){
cnt = -1;
break;
}
if((up->type == TYPE_AX25I || up->type == TYPE_NETROML4)
&& uchar(c) == '\r')
c = '\n';
*buf++ = c;
cnt++;
if(uchar(c) == '\n')
break;
}
*buf = '\0';
return cnt;
}
/* Do printf on a user socket */
#ifdef ANSIPROTO
int
usprintf(int s,char *fmt,...)
{
register struct mbuf *bp;
int len;
va_list args;
va_start(args,fmt);
bp = alloc_mbuf(200);
len = vsprintf(bp->data,fmt,args);
bp->cnt = strlen(bp->data);
send_mbuf(s,bp,0,NULLCHAR,0);
va_end(args);
return len;
}
#else
/*VARARGS*/
int
usprintf(s,fmt,arg1,arg2,arg3,arg4,arg5,arg6)
int s; /* Socket index */
char *fmt; /* Message format */
int arg1,arg2,arg3; /* Arguments */
int arg4,arg5,arg6;
{
register struct mbuf *bp;
int len;
bp = alloc_mbuf(200);
len = sprintf(bp->data,fmt,arg1,arg2,arg3,arg4,arg5,arg6);
bp->cnt = strlen(bp->data);
send_mbuf(s,bp,0,NULLCHAR,0);
return len;
}
#endif
/* Convert a socket (address + port) to an ascii string of the form
* aaa.aaa.aaa.aaa:ppppp
*/
char *
psocket(p)
void *p; /* Pointer to structure to decode */
{
#if defined(AX25) || defined(NETROM)
char tmp[11];
#endif
static char buf[30];
union sp sp;
struct socket socket;
sp.p = p;
switch(sp.sa->sa_family){
case AF_INET:
socket.address = sp.in->sin_addr.s_addr;
socket.port = sp.in->sin_port;
strcpy(buf,pinet(&socket));
break;
#ifdef AX25
case AF_AX25:
pax25(tmp,&sp.ax->ax25_addr);
sprintf(buf,"%s on %s",tmp,sp.ax->iface);
break;
#endif
#ifdef NETROM
case AF_NETROM:
pax25(tmp,(struct ax25_addr *)sp.nr->nr_addr.user_call);
sprintf(buf,"%s @ ",tmp);
pax25(tmp,(struct ax25_addr *)sp.nr->nr_addr.node_call);
strcat(buf,tmp);
break;
#endif
}
return buf;
}
/* Read a character from a socket with stream buffering */
int
recvchar(s,bpp)
int s; /* Socket index */
struct mbuf **bpp; /* User-supplied buffer pointer */
{
char c;
if(*bpp == NULLBUF)
recv_mbuf(s,bpp,0,0,NULLCHAR,0); /* Replenish */
if(pullup(bpp,&c,1) != 1)
return -1;
return uchar(c);
}
/* Blow away all sockets belonging to a certain process. Used by killproc(). */
void
freesock(pp)
struct proc *pp;
{
register struct usock *up;
register int i;
for(i=0;i < Nusock;i++){
up = &Usock[i];
if(up->type != NOTUSED && up->owner == pp)
shutdown(i,2);
}
}
/* End of user utility subroutines, start internal subroutines */
/* Raw IP receive upcall routine */
void
rip_recv(rp)
struct raw_ip *rp;
{
psignal(itop(rp->user),1);
pwait(NULL);
}
/* UDP receive upcall routine */
static void
s_urcall(udp,cnt)
struct udp_cb *udp;
int cnt;
{
psignal(itop(udp->user),1);
pwait(NULL);
}
/* TCP receive upcall routine */
static void
s_trcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
/* Wake up anybody waiting for data, and let them run */
psignal(itop(tcb->user),1);
pwait(NULL);
}
/* TCP transmit upcall routine */
static void
s_ttcall(tcb,cnt)
struct tcb *tcb;
int cnt;
{
/* Wake up anybody waiting to send data, and let them run */
psignal(itop(tcb->user),1);
pwait(NULL);
}
/* TCP state change upcall routine */
static void
s_tscall(tcb,old,new)
struct tcb *tcb;
int old,new;
{
int s,ns;
struct usock *up,*nup,*oup;
union sp sp;
s = tcb->user;
oup = up = itop(s);
switch(new){
case CLOSED:
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this tcb so they will notice it disappearing.
*/
if(up != NULLUSOCK){
up->cb.tcb = NULLTCB;
up->errcodes[0] = tcb->reason;
up->errcodes[1] = tcb->type;
up->errcodes[2] = tcb->code;
}
del_tcp(tcb);
break;
case SYN_RECEIVED:
/* Handle an incoming connection. If this is a server TCB,
* then we're being handed a "clone" TCB and we need to
* create a new socket structure for it. In either case,
* find out who we're talking to and wake up the guy waiting
* for the connection.
*/
if(tcb->flags.clone){
/* Clone the socket */
ns = socket(AF_INET,SOCK_STREAM,0);
nup = &Usock[ns];
ASSIGN(*nup,*up);
tcb->user = ns;
nup->cb.tcb = tcb;
/* Allocate new memory for the name areas */
nup->name = malloc(SOCKSIZE);
nup->peername = malloc(SOCKSIZE);
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
s = ns;
} else {
/* Allocate space for the peer's name */
up->peername = malloc(SOCKSIZE);
/* Store the old socket # in the old socket */
up->rdysock = s;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = up->cb.tcb->conn.local.address;
sp.in->sin_port = up->cb.tcb->conn.local.port;
up->namelen = SOCKSIZE;
sp.p = up->peername;
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = up->cb.tcb->conn.remote.address;
sp.in->sin_port = up->cb.tcb->conn.remote.port;
up->peernamelen = SOCKSIZE;
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
break;
default: /* Ignore all other state transitions */
break;
}
psignal(up,0); /* In case anybody's waiting */
}
/* Discard data received on a TCP connection. Used after a receive shutdown or
* close_s until the TCB disappears.
*/
static void
trdiscard(tcb,cnt)
struct tcb *tcb;
int cnt;
{
struct mbuf *bp;
recv_tcp(tcb,&bp,cnt);
free_p(bp);
}
#ifdef AX25
/* AX.25 receive upcall */
void
s_arcall(axp,cnt)
struct ax25_cb *axp;
int cnt;
{
int ns;
struct usock *up,*nup,*oup;
union sp sp;
up = itop(axp->user);
/* When AX.25 data arrives for the first time the AX.25 listener
is notified, if active. If the AX.25 listener is a server its
socket is duplicated in the same manner as in s_tscall().
*/
if (Axi_sock != -1 && axp->user == -1) {
oup = up = itop(Axi_sock);
/* From now on, use the same upcalls as the listener */
axp->t_upcall = up->cb.ax25->t_upcall;
axp->r_upcall = up->cb.ax25->r_upcall;
axp->s_upcall = up->cb.ax25->s_upcall;
if (up->cb.ax25->flags.clone) {
/* Clone the socket */
ns = socket(AF_AX25,SOCK_STREAM,0);
nup = &Usock[ns];
ASSIGN(*nup,*up);
axp->user = ns;
nup->cb.ax25 = axp;
/* Allocate new memory for the name areas */
nup->name = malloc(sizeof(struct sockaddr_ax));
nup->peername = malloc(sizeof(struct sockaddr_ax));
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
} else {
axp->user = Axi_sock;
del_ax25(up->cb.ax25);
up->cb.ax25 = axp;
/* Allocate space for the peer's name */
up->peername = malloc(sizeof(struct sockaddr_ax));
/* Store the old socket # in the old socket */
up->rdysock = Axi_sock;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.ax->sax_family = AF_AX25;
ASSIGN(sp.ax->ax25_addr,axp->local);
memcpy(sp.ax->iface,axp->iface->name,ILEN);
up->namelen = sizeof(struct sockaddr_ax);
sp.p = up->peername;
sp.ax->sax_family = AF_AX25;
ASSIGN(sp.ax->ax25_addr,axp->remote);
memcpy(sp.ax->iface,axp->iface->name,ILEN);
up->peernamelen = sizeof(struct sockaddr_ax);
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
return;
}
/* Wake up anyone waiting, and let them run */
psignal(up,1);
pwait(NULL);
}
/* AX.25 transmit upcall */
void
s_atcall(axp,cnt)
struct ax25_cb *axp;
int cnt;
{
/* Wake up anyone waiting, and let them run */
psignal(itop(axp->user),1);
pwait(NULL);
}
/* AX25 state change upcall routine */
void
s_ascall(axp,old,new)
register struct ax25_cb *axp;
int old,new;
{
int s;
struct usock *up;
s = axp->user;
up = itop(s);
switch(new){
case DISCONNECTED:
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this block so they will notice it disappearing.
*/
if(up != NULLUSOCK){
up->errcodes[0] = axp->reason;
up->cb.ax25 = NULLAX25;
}
del_ax25(axp);
break;
default: /* Other transitions are ignored */
break;
}
psignal(up,0); /* In case anybody's waiting */
}
#endif
#ifdef NETROM
/* NET/ROM receive upcall routine */
static void
s_nrcall(cb,cnt)
struct nr4cb *cb;
int cnt;
{
/* Wake up anybody waiting for data, and let them run */
psignal(itop(cb->user),1);
pwait(NULL);
}
/* NET/ROM transmit upcall routine */
static void
s_ntcall(cb,cnt)
struct nr4cb *cb;
int cnt;
{
/* Wake up anybody waiting to send data, and let them run */
psignal(itop(cb->user),1);
pwait(NULL);
}
/* NET/ROM state change upcall routine */
static void
s_nscall(cb,old,new)
struct nr4cb *cb;
int old,new;
{
int s,ns;
struct usock *up,*nup,*oup;
union sp sp;
s = cb->user;
oup = up = itop(s);
if(new == NR4STDISC && up != NULLUSOCK){
/* Clean up. If the user has already closed the socket,
* then up will be null (s was set to -1 by the close routine).
* If not, then this is an abnormal close (e.g., a reset)
* and clearing out the pointer in the socket structure will
* prevent any further operations on what will be a freed
* control block. Also wake up anybody waiting on events
* related to this cb so they will notice it disappearing.
*/
up->cb.nr4 = NULLNR4CB;
up->errcodes[0] = cb->dreason;
}
if(new == NR4STCON && old == NR4STDISC){
/* Handle an incoming connection. If this is a server cb,
* then we're being handed a "clone" cb and we need to
* create a new socket structure for it. In either case,
* find out who we're talking to and wake up the guy waiting
* for the connection.
*/
if(cb->clone){
/* Clone the socket */
ns = socket(AF_NETROM,SOCK_SEQPACKET,0);
nup = &Usock[ns];
ASSIGN(*nup,*up);
cb->user = ns;
nup->cb.nr4 = cb;
cb->clone = 0; /* to avoid getting here again */
/* Allocate new memory for the name areas */
nup->name = malloc(sizeof(struct sockaddr_nr));
nup->peername = malloc(sizeof(struct sockaddr_nr));
/* Store the new socket # in the old one */
up->rdysock = ns;
up = nup;
s = ns;
} else {
/* Allocate space for the peer's name */
up->peername = malloc(sizeof(struct sockaddr_nr));
/* Store the old socket # in the old socket */
up->rdysock = s;
}
/* Load the addresses. Memory for the name has already
* been allocated, either above or in the original bind.
*/
sp.p = up->name;
sp.nr->nr_family = AF_NETROM;
ASSIGN(sp.nr->nr_addr,up->cb.nr4->local);
up->namelen = sizeof(struct sockaddr_nr);
sp.p = up->peername;
sp.nr->nr_family = AF_NETROM;
ASSIGN(sp.nr->nr_addr,up->cb.nr4->remote);
up->peernamelen = sizeof(struct sockaddr_nr);
/* Wake up the guy accepting it, and let him run */
psignal(oup,1);
pwait(NULL);
}
/* Ignore all other state transitions */
psignal(up,0); /* In case anybody's waiting */
}
#endif
/* Verify address family and length according to the socket type */
static int
checkaddr(type,name,namelen)
int type;
char *name;
int namelen;
{
register struct sockaddr *sp;
sp = (struct sockaddr *)name;
/* Verify length and address family according to protocol */
switch(type){
case TYPE_TCP:
case TYPE_UDP:
if(sp->sa_family != AF_INET
|| namelen != sizeof(struct sockaddr_in))
return -1;
break;
case TYPE_AX25I:
case TYPE_AX25UI:
if(sp->sa_family != AF_AX25
|| namelen != sizeof(struct sockaddr_ax))
return -1;
break;
case TYPE_NETROML3:
case TYPE_NETROML4:
if(sp->sa_family != AF_NETROM
|| namelen != sizeof(struct sockaddr_nr))
return -1;
break;
}
return 0;
}
/* Convert a socket index to an internal user socket structure pointer */
static struct usock *
itop(s)
register int s; /* Socket index */
{
register struct usock *up;
if(s < 0 || s >= Nusock)
return NULLUSOCK;
up = &Usock[s];
if(up->type == NOTUSED)
return NULLUSOCK;
return up;
}
/* Issue an automatic bind of a local address */
static void
autobind(s,af)
int s,af;
{
char buf[MAXSOCKSIZE];
union sp sp;
sp.p = buf;
switch(af){
case AF_INET:
sp.in->sin_family = AF_INET;
sp.in->sin_addr.s_addr = Ip_addr;
sp.in->sin_port = Lport++;
bind(s,sp.p,sizeof(struct sockaddr_in));
break;
#ifdef AX25
case AF_AX25:
sp.ax->sax_family = AF_AX25;
memcpy(sp.ax->ax25_addr.call,Mycall.call,ALEN);
sp.ax->ax25_addr.ssid = Mycall.ssid;
bind(s,sp.p,sizeof(struct sockaddr_ax));
break;
#endif
#ifdef NETROM
case AF_NETROM:
sp.nr->nr_family = AF_NETROM;
memcpy(sp.nr->nr_addr.user_call,&Mycall,AXALEN);
memcpy(sp.nr->nr_addr.node_call,&Mycall,AXALEN);
bind(s,sp.p,sizeof(struct sockaddr_nr));
break;
#endif
}
}